/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package we.dispatcher;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import we.common.CDebug;
import we.config.CConstant;
import we.dispatcher.reader.IReadingProcessor;
import we.exception.ReadingPacketException;
import we.exception.UnsupportedTypeException;

/**
 *
 * @author trungdv2
 */
public class CPacketReader implements IDisposable
{

    public static void printBuffRead(String msg, ByteBuffer buff)
    {
        CDebug.Trace(CPacketReader.class, "---- " + msg);
        CDebug.Trace(CPacketReader.class, "lenthg   : " + buff.position());
        CDebug.Trace(CPacketReader.class, "pos      : " + buff.position());
        CDebug.Trace(CPacketReader.class, "limit    : " + buff.limit());
        StringBuilder string = new StringBuilder();
        for (int i = 0; i < buff.position(); i++)
        {
            string.append(buff.get(i) + ", ");
        }
        CDebug.Trace(CPacketReader.class, string);
    }

    public static void printBuffWrite(String msg, ByteBuffer buff)
    {
        CDebug.Trace(CPacketReader.class, "---- " + msg);
        CDebug.Trace(CPacketReader.class, "length     : " + (buff.limit() - buff.position()));
        CDebug.Trace(CPacketReader.class, "pos        : " + buff.position());
        CDebug.Trace(CPacketReader.class, "limit      : " + buff.limit());
        StringBuilder string = new StringBuilder();
        for (int i = buff.position(); i < buff.limit(); i++)
        {
            string.append(buff.get(i) + ", ");
        }
        CDebug.Trace(CPacketReader.class, string);
    }
    private final static Charset charset = Charset.forName("UTF-8");
    ByteBuffer buffer;
    private int length;
    private boolean completed;
    private int id;
    public boolean idle;

    public CPacketReader(int maxLength)
    {
        this(maxLength, false);
    }

    public ByteBuffer buffer()
    {
        return buffer;
    }

    public CPacketReader(int maxLength, boolean directBuffer)
    {
        if (directBuffer)
        {
            buffer = ByteBuffer.allocateDirect(maxLength);
        } else
        {
            buffer = ByteBuffer.allocate(maxLength);
        }

        buffer.order(ByteOrder.LITTLE_ENDIAN);
        clearAll();
    }

    public void clearAll()
    {
        buffer.clear();
        length = -1;
        completed = false;
        id = -1;
        idle = true;
    }

    public boolean checkCompletedPackage() throws ReadingPacketException
    {
        boolean result = false;
        buffer.flip();
        if (length < 0)
        {
            if (buffer.remaining() >= 4)
            {
                length = buffer.getInt();
            }
        }

        if (length > CConstant.MAX_MESSAGE_LENGTH)
        {
            throw new ReadingPacketException("do dai length > max length cua goi tin : " + length + " < " + CConstant.MAX_MESSAGE_LENGTH);
        } else
        {
            if (buffer.remaining() + 4 == length)
            {
                result = true;
            }
        }

        buffer.compact();
        if (result)
        {
            completed = true;
        }
        return result;
    }

    public int readID() throws ReadingPacketException
    {
        if (!completed)
        {
            throw new ReadingPacketException("gói tin đọc chưa completed. Gọi checkCompletedPackage() đến khi trả về true");
        }
        if (id >= 0)
        {
            throw new ReadingPacketException("Bạn đã đọc ID gói tin này rồi.");
        }
        buffer.flip();
        id = buffer.getInt();
        return id;
    }

    public Object[] readByProcessor(IReadingProcessor... processors) throws UnsupportedTypeException, ReadingPacketException
    {
        if (id < 0)
        {
            throw new ReadingPacketException("Bạn phải gọi hàm readID() trước.");
        }
        Object[] result = new Object[processors.length];
        for (int i = 0; i < processors.length; i++)
        {
            result[i] = processors[i].read(this);
            if (result[i] == null)
            {
                throw new UnsupportedTypeException("processor không hỗ trợ đọc kiểu này : " + processors.getClass().getName(), new Throwable());
            }
        }
        if (buffer.hasRemaining())
        {
            throw new ReadingPacketException("Đọc chưa hết gói tin. Buffer vẫn còn.");
        }
        clearAll();
        return result;
    }

    public Object[] read(Class<?>... types) throws UnsupportedTypeException, ReadingPacketException
    {
        if (id < 0)
        {
            throw new ReadingPacketException("Bạn phải gọi hàm readID() trước.");
        }
        Object[] result = new Object[types.length];
        for (int i = 0; i < types.length; i++)
        {
            Class<?> _class = types[i];
            if (_class == String.class)
            {
                result[i] = readString();
            } else if (_class == Byte.class)
            {
                result[i] = buffer.get();
            } else if (_class == Integer.class)
            {
                result[i] = buffer.getInt();
            } else if (_class == Long.class)
            {
                result[i] = buffer.getLong();
            } else if (_class == Short.class)
            {
                result[i] = buffer.getShort();
            } else
            {
                throw new UnsupportedTypeException("Chưa hỗ trợ đọc object kiểu : " + _class.getName(), new Throwable());
            }
            //System.out.println(" read : " + result[i + 1]);
        }

        if (buffer.hasRemaining())
        {
            throw new ReadingPacketException("Đọc chưa hết gói tin. Buffer vẫn còn.");
        }
        clearAll();
        return result;
    }

    public String readString()
    {
        byte[] arr = new byte[buffer.getShort()];
        buffer.get(arr);
        return new String(arr, charset);
    }

    public String readString(int maxLength)
    {
        byte[] arr = new byte[maxLength];
        buffer.get(arr);
        int i;
        for (i = arr.length - 1; i >= 0; i--)
        {
            if (arr[i] != 0)
            {
                break;
            }
        }
        return new String(arr, 0, i + 1, charset);
    }

    public void put(byte b)
    {
        buffer.put(b);
    }

    @Override
    public void dispose()
    {
        buffer = null;
    }
    
    public int GetInt(){
        return buffer.getInt();
    }
    
    public double GetDouble(){
        return buffer.getDouble();
    }
}
